# -*- coding: utf-8 -*-
#!/usr/bin/python

#Python 3
from __future__ import division
#ArgV
from sys import argv
#OpenGL
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
#Math
from math import sin,cos,pi

#FIX Glut Menu
import ctypes
from OpenGL.raw import GLUT as simple
MENUFUNC = ctypes.CFUNCTYPE(None, ctypes.c_int)
def fixed_glutCreateMenu(menu_func):
    """replacement for broken glutCreateMenu"""
    menu = simple.glutCreateMenu(MENUFUNC(menu_func))
    return menu
glutCreateMenu = fixed_glutCreateMenu

#Tecla ESC
GLUT_KEY_ESC = '\033'

#Classe de Obejeto na Tela
class Form3d:
	def __init__(self):
		self.x=0.
		self.y=0.
		self.z=0.
		self.R=1.
		self.G=1.
		self.B=1.
		self.A=1.
		self.xs=1.
		self.ys=1.
		self.zs=1.
		self.r1=0.
		self.r2=0.
		self.selected=False
	def display(self):
		glPushMatrix ()
		glTranslatef (self.x, self.y, self.z)
		glRotatef (self.r1, 1.0, 0.0, 0.0)
		glRotatef (self.r2, 0.0, 1.0, 0.0)
		#glRotatef (sin(self.r2), 0.0, 0.0, 1.0)
		if self.A <1. :
			glEnable(GL_BLEND)
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
			
		if self.selected:
			glColor4f(1.-self.R, 1.-self.G, 1.-self.B,1.0)
		else:
			glColor4f(self.R, self.G, self.B,self.A)
		glScalef(self.xs, self.ys, self.zs); 
		self.setup_object()
		self.display_object()
		glPopMatrix ()
	def display_object(self):
		glutSolidSphere(0.5,30,30)
	def setup_object(self):
		especularidade=[1.0,1.0,1.0,1.0]
		especMaterial = 60
		# Define a refletância do material 
		glMaterialfv(GL_FRONT,GL_SPECULAR, especularidade)
		# Define a concentração do brilho
		glMateriali(GL_FRONT,GL_SHININESS,especMaterial)
	def set_color(self,R,G,B,A):
		self.R=R
		self.G=G
		self.B=B
		self.A=A
	def set_pos(self,x,y,z):
		self.x=x
		self.y=y
		self.z=z
	def set_scale(self,x,y,z):
		self.xs=x
		self.ys=y
		self.zs=z
	def set_rotate(self,x,y,z):
		self.xr=x
		self.yr=y
		self.zr=z
	def trans_x(self,s):
		self.x+=s*0.25
	def trans_y(self,s):
		self.y+=s*0.25
	def trans_z(self,s):
		self.z+=s*0.25

#Classe de Esfera
class Sphere(Form3d):
	def display_object(self):
		glutSolidSphere(0.5,30,30)
	def setup_object(self):
		#float[] no_mat = {0.0f, 0.0f, 0.0f, 1.0f};
		#float[] mat_ambient = {0.7f, 0.7f, 0.7f, 1.0f};
		#float[] mat_ambient_color = {0.8f, 0.8f, 0.2f, 1.0f};
		#float[] mat_diffuse = {0.1f, 0.5f, 0.8f, 1.0f};
		#float[] mat_specular = {1.0f, 1.0f, 1.0f, 1.0f};
		#float no_shininess = 0.0f;
		#float low_shininess = 5.0f;
		#float high_shininess = 100.0f;
		#float[] mat_emission = {0.3f, 0.2f, 0.2f, 0.0f};
		glMaterialfv(GL_FRONT, GL_AMBIENT, [0.,0.,0.,1.]);	#Ambiente
		glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.1,0.5,0.8,1.]);	#Difusão
		glMaterialfv(GL_FRONT, GL_SPECULAR, [0.,0.,0.,1.]);	#Reflexão
		glMaterialf(GL_FRONT, GL_SHININESS, 0.);		#Concentração
		glMaterialfv(GL_FRONT, GL_EMISSION, [0.,0.,0.,1.]);	#Emissão

#Classe de Cubo
class Cube(Form3d):
	def display_object(self):
		glutSolidCube(1.0)
	def setup_object(self):
		glMaterialfv(GL_FRONT, GL_AMBIENT, [0.,0.,0.,1.]);
		glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.1,0.5,0.8,1.]);
		glMaterialfv(GL_FRONT, GL_SPECULAR, [0.,1.,1.,1.]);
		glMaterialf(GL_FRONT, GL_SHININESS, 5.);
		glMaterialfv(GL_FRONT, GL_EMISSION, [0.,0.,0.,1.]);

#Classe de Chaleira
class Teapot(Form3d):
	def display_object(self):
		glutSolidTeapot(0.5)
	def setup_object(self):
		especularidade=[1.0,1.0,1.0,1.0]
		especMaterial = 60
		# Define a refletância do material 
		glMaterialfv(GL_FRONT,GL_SPECULAR, especularidade)
		# Define a concentração do brilho
		glMateriali(GL_FRONT,GL_SHININESS,especMaterial)

#Classe da Camera
class Cam:
	def __init__(self):
		#Origem
		self.r=10.
		self.angle1=pi/2
		self.angle2=0.0
		#Destino
		self.x=0.
		self.y=0.
		self.z=0.
	def zoom(self,s):
		if s>(self.r*(-1)):
			self.r+=s
			if self.r>50:
				self.r=50
	def rot1(self,s):
		self.angle1+=s*0.5
	def rot2(self,s):
		self.angle2+=s*0.5
	def setup(self):
		# Especifica sistema de coordenadas de projeção
		glMatrixMode(GL_PROJECTION)
		# Inicializa sistema de coordenadas de projeção
		glLoadIdentity()
		# Especifica a projeção perspectiva
		gluPerspective(45,GLUT_WINDOW_HEIGHT/GLUT_WINDOW_WIDTH,0.1,500)
		# Especifica sistema de coordenadas do modelo
		glMatrixMode(GL_MODELVIEW)
		# Inicializa sistema de coordenadas do modelo
		glLoadIdentity()
		# Especifica posição do observador e do alvo
		gluLookAt(self.r*cos(self.angle1)*cos(self.angle2),self.r*sin(self.angle2),self.r*sin(self.angle1)*cos(self.angle2),self.x,self.y,self.z,0.,1.,0.)

#Globais
cube=Cube()
cube.set_color(1.,0.,0.,1.)
cube.set_pos(0.,0.,0.)

sphere=Sphere()
sphere.set_color(0.,0.,1.,1.)
sphere.set_pos(2.,0.,0.)

teapot=Teapot()
teapot.set_color(0.,1.,0.,0.50)
teapot.set_pos(-2.,0.,0.)

selected=None

cam=Cam()

def screen(x,y,w,h,title):
	"Cria Janela e configura objetos"
	#Double Buffer, RGBA e Depth
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH)
	#Tamanho
	glutInitWindowSize (w, h)
	#Posição
	glutInitWindowPosition (x, y)
	#Cria Janela e seta Titulo
	glutCreateWindow (title)
	#Seta Icone
	#glutSetIconTitle("./icon.png")
	#Configura Iluminação
	setup_light()
	#Seta função de desenho
	glutDisplayFunc (display)
	#Seta função de redimensionamento
	glutReshapeFunc (reshape_listener)
	#Listener do Teclado
	glutKeyboardFunc(key_listener)
	glutSpecialFunc(key_listener)
	#Listener do Mouse
	glutMouseFunc(mouse_listener)
	#Listener do Scroll do Mouse
	glutMouseWheelFunc(mouse_wheel_listener)
	#Cria Menu
	create_menu()
	#Show
	glutMainLoop ()

def display():
	glClearColor (0.0,0.0,0.0,1.0)
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

	global sphere
	sphere.display()
	global cube
	cube.display()
	global teapot
	teapot.display()
	
	glutSwapBuffers()

def reshape_listener (w,h):
	# Para previnir uma divisão por zero
	#if h == 0:
	#	h = 1
	
	# Especifica o tamanho da viewport
	#glViewport(0, 0, w, h)
	
	global cam
	cam.setup()

def key_listener(key,x,y):
	#print "XXX",key
	global selected
	global cam
	if key=='q':
		exit(0)
	elif key==GLUT_KEY_UP:
		if selected!=None:
			selected.r1+=5.
		else:
			cam.rot2(1.)
	elif key==GLUT_KEY_DOWN:
		if selected!=None:
			selected.r1-=5.
		else:
			cam.rot2(-1.)
	elif key==GLUT_KEY_RIGHT:
		if selected!=None:
			selected.r2+=5.
		else:
			cam.rot1(1.)
	elif key==GLUT_KEY_LEFT:
		if selected!=None:
			selected.r2-=5.
		else:
			cam.rot1(-1.)
	elif key==GLUT_KEY_ESC:
		if selected!=None:
			selected.selected=False
			selected=None
		else:
			exit(0)
	elif key==GLUT_KEY_F1:
		if selected!=None:
			selected.selected=False
		if selected!=teapot:
			selected=teapot
			selected.selected=True
		else:
			selected=None
	elif key==GLUT_KEY_F2:
		if selected!=None:
			selected.selected=False
		if selected!=cube:
			selected=cube
			selected.selected=True
		else:
			selected=None
	elif key==GLUT_KEY_F3:
		if selected!=None:
			selected.selected=False
		if selected!=sphere:
			selected=sphere
			selected.selected=True
		else:
			selected=None
	elif key=='8':
		if selected!=None:
			selected.trans_y(1.)
		else:
			pass
	elif key=='2':
		if selected!=None:
			selected.trans_y(-1.)
		else:
			pass
	elif key=='4':
		if selected!=None:
			selected.trans_x(-1.)
		else:
			pass
	elif key=='6':
		if selected!=None:
			selected.trans_x(1.)
		else:
			pass
	elif key=='+':
		if selected!=None:
			selected.trans_z(1.)
		else:
			cam.zoom(1.)
	elif key=='-':
		if selected!=None:
			selected.trans_z(-1.)
		else:
			cam.zoom(-1.)
	cam.setup()
	glutPostRedisplay()
	#switch (key) {
		#case 'h':
			#glMatrixMode (GL_MODELVIEW);        /* manipulate modelview matrix  */
			#glRotatef(15.0, 0.0,1.0,0.0);
			#break;
		#case 'j':
			#glMatrixMode (GL_MODELVIEW);        /* manipulate modelview matrix  */
			#glRotatef(15.0, 1.0,0.0,0.0);
			#break;
		#case 'k':
			#glMatrixMode (GL_MODELVIEW);        /* manipulate modelview matrix  */
			#glRotatef(-15.0, 1.0,0.0,0.0);
			#break;
		#case 'l':
			#glMatrixMode (GL_MODELVIEW);        /* manipulate modelview matrix  */
			#glRotatef(-15.0, 0.0,1.0,0.0);
			#break;
		#case 27:           /* Esc will quit */
			#exit(1);
			#break;
		#default:
			#break;
	#}

def mouse_listener(button,state,x,y):
	print "MOUSE",x,y
	if state == GLUT_DOWN:
		pass

def mouse_wheel_listener(wheel_number,direction,x,y):
	#print wheel_number,direction,x,y
	global cam
	cam.zoom(direction)
	cam.setup()
	glutPostRedisplay()

def menu_listener(value):
	if value==1:
		exit(0)

def menu_obj_listener(value):
	global selected
	if selected!=None:
		selected.selected=False
	if value==0:
		selected=None
	elif value==1:
		selected=teapot
		selected.selected=True
	elif value==2:
		selected=cube
		selected.selected=True
	elif value==3:
		selected=sphere
		selected.selected=True
	glutPostRedisplay()

def create_menu():
	obj_menu=glutCreateMenu(menu_obj_listener)
	glutAddMenuEntry("None", 0)
	glutAddMenuEntry("Teapot", 1)
	glutAddMenuEntry("Cube", 2)
	glutAddMenuEntry("Sphere", 3)
	
	glutCreateMenu(menu_listener)
	glutAddSubMenu("Select Object", obj_menu);
	glutAddMenuEntry("Quit", 1)
	glutAttachMenu(GLUT_RIGHT_BUTTON)

def setup_light():
	luzAmbiente=[0.2,0.2,0.2,1.0]
	luzDifusa=[0.7,0.7,0.7,1.0]	   # "cor" 
	luzEspecular=[1.0, 1.0, 1.0, 1.0]# "brilho" 
	posicaoLuz=[0.0, 50.0, 50.0, 1.0]

	# Capacidade de brilho do material
	especularidade=[1.0,1.0,1.0,1.0] 
	especMaterial = 60

 	# Especifica que a cor de fundo da janela será preta
	glClearColor(0.0, 0.0, 0.0, 1.0)
	
	# Habilita o modelo de colorização de Gouraud
	glShadeModel(GL_SMOOTH)

	# Define a refletância do material 
	glMaterialfv(GL_FRONT,GL_SPECULAR, especularidade)
	# Define a concentração do brilho
	glMateriali(GL_FRONT,GL_SHININESS,especMaterial)

	# Ativa o uso da luz ambiente 
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, luzAmbiente)

	# Define os parâmetros da luz de número 0
	glLightfv(GL_LIGHT0, GL_AMBIENT, luzAmbiente)
	glLightfv(GL_LIGHT0, GL_DIFFUSE, luzDifusa )
	glLightfv(GL_LIGHT0, GL_SPECULAR, luzEspecular )
	glLightfv(GL_LIGHT0, GL_POSITION, posicaoLuz )

	# Habilita a definição da cor do material a partir da cor corrente
	glEnable(GL_COLOR_MATERIAL)
	#Habilita o uso de iluminação
	glEnable(GL_LIGHTING)
	# Habilita a luz de número 0
	glEnable(GL_LIGHT0)
	# Habilita o depth-buffering
	glEnable(GL_DEPTH_TEST)


if __name__ == '__main__':
	glutInit(argv)
	screen(100,100,800,600,"Projeto 1 - MC930 / MO603")